#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>

#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>

#include "nocopy.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
// #include "Task.hpp"

static const int default_backlog = 5;
#define CONV(addr_ptr) ((sockaddr *)addr_ptr)
using task_t = std::function<void()>;
using callback_t = std::function<void(int, const InetAddr &)>;

class TcpServer;
class ThreadData_v3
{
public:
    ThreadData_v3(int sockfd, TcpServer *ptr, sockaddr_in &peer)
        : _sockfd(sockfd), _ptr(ptr), _addr(peer)
    {
    }
    ~ThreadData_v3()
    {
        close(_sockfd);
    }

public:
    int _sockfd;
    TcpServer *_ptr;
    InetAddr _addr; // 拿到客户端的socket
};

class TcpServer : public nocopy
{
public:
    TcpServer(uint16_t port)
        : _port(port)
    {
    }

    void Init() // 初始化监听套接字
    {
        // 1.
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            exit(1);
        }
        // 2.服务端主动关闭连接，无法重启（出现bind失败）
        int opt = 1; // 设置套接字属性
        setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);
        // 3.
        sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(_port);
        if (bind(_listensockfd, CONV(&server), sizeof server) != 0)
        {
            exit(2);
        }

        if (listen(_listensockfd, default_backlog) != 0)
        {
            exit(3);
        }
        ThreadPool<task_t>::GetInstance()->Start(); // 传函数对象
    }
    // void Server(int sockfd)
    // {
    //     char buffer[1024];
    //     while(true)
    //     {
    //         int n = read(sockfd, buffer, sizeof buffer - 1);
    //         if(n > 0)
    //         {
    //             buffer[n] = 0;
    //             std::cout << "client # " << buffer << std::endl;
    //             std::string backbuffer = "server # ";
    //             backbuffer += buffer;
    //             write(sockfd, backbuffer.c_str(), backbuffer.size());
    //         }
    //         else if(n == 0) //读完，对端关闭
    //         {
    //             std::cout << "client quit" << std::endl;
    //             break;
    //         }
    //         else
    //         {
    //             lg.LogMessage(Error, "read sockfd error, errno: %d, error: %s\n", errno, strerror(errno));
    //             break;
    //         }
    //     }
    // }

    void Server(int sockfd, const InetAddr &addr)
    {
        char buffer[1024];
        while (true)
        {
            int n = read(sockfd, buffer, sizeof buffer - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << addr.GetDebug() << "# " << buffer << std::endl;

                std::string backbuffer = "server # ";
                backbuffer += buffer;
                write(sockfd, backbuffer.c_str(), backbuffer.size());
            }
            else if (n == 0) // 读完，对端关闭
            {
                std::cout << "client quit" << std::endl;
                break;
            }
            else
            {
                lg.LogMessage(Error, "read sockfd error, errno: %d, error: %s\n", errno, strerror(errno));
                break;
            }
        }
    }
    void Start()
    {
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof peer;
            int sockfd = accept(_listensockfd, CONV(&peer), &len); // 阻塞式的
            if (sockfd < 0)                                        // 链接失败重新连接
            {
                continue;
            }
            lg.LogMessage(Debug, "accept success... sockfd: %d\n", sockfd);

            // bug:一次只能一个连，这是单进程的
            //  Server(sockfd);
            //  close(sockfd);

            // v1 多进程（等待版本）
            //  pid_t id = fork();
            //  if(id < 0)
            //  {
            //      close(sockfd);
            //      continue;
            //  }
            //  else if(id == 0)
            //  {
            //      close(_listensockfd);
            //      if(fork()) exit(0); //退出子进程

            //     //孙子进程执行网络通信
            //     Server(sockfd); //孙子进程现在是孤儿进程，被1号进程领养
            //     //我觉得下面两步不用，因为这里孙子进程已经退了
            //     close(sockfd); //写了也好，防止后续加代码影响
            //     exit(0);
            // }
            // else
            // {
            //     close(sockfd); //下一次的fd还是4，防止出现文件描述符泄露，
            //     pid_t rid = waitpid(id, nullptr, 0);  //直接回收子进程，因为已经退了
            // }

            // v2 多进程（不等待）信号
            //  signal(SIGCHLD, SIG_IGN); //忽略这个信号，子进程退出自动释放自己的资源，防止僵尸进程
            //  pid_t id = fork();
            //  if(id < 0)
            //  {
            //      close(sockfd);
            //      continue;
            //  }
            //  else if(id == 0)
            //  {
            //      close(_listensockfd);

            //     Server(sockfd);
            //     close(sockfd);
            //     exit(0);
            // }
            // else
            // {
            //     close(sockfd);
            // }

            // v3 多线程
            //  ThreadData_v3* td = new ThreadData_v3(sockfd, this, peer);        //目前不明白为什么这样就线程安全了，因为只有一个子线程，<-不是
            //  pthread_t tid;
            //  //普通的函数指针（C风格函数指针），
            //  //而 std::bind 返回的是一个 可调用对象（函数仿函数） ，并不是一个函数指针
            //  // pthread_create(&tid, nullptr, std::bind(&TcpServer::HandlerRoutine, this, std::placeholders::_1), td);
            //  pthread_create(&tid, nullptr, HandlerRoutine, td);

            // v4 线程池★★★
            //这里有个问题，为什么一个客户端会启动两个线程？？？
            // 还是分开比较好
            //  ThreadPool<task_t>::GetInstance()->Push(std::bind(&TcpServer::Server, this, sockfd, peer));
            // task_t task = std::bind(&TcpServer::Server, this, sockfd, peer); // server的加上const修饰这样就可以直接传peer，
            // ThreadPool<task_t>::GetInstance()->Push(task);                   // 链接个数取决于线程池中线程的个数
        
            //v5 功能路由
            task_t task = std::bind(&TcpServer::Routine, this, sockfd, peer); 
            ThreadPool<task_t>::GetInstance()->Push(task);     
        }
    }

    static void *HandlerRoutine(void *args)
    {
        ThreadData_v3 *td = static_cast<ThreadData_v3 *>(args);
        pthread_detach(pthread_self());
        // Server(td->_sockfd); //静态成员函数不能调用非静态成员函数
        // td->_ptr->Server(td->_sockfd);
        td->_ptr->Server(td->_sockfd, td->_addr);

        // close(td->_sockfd); //这个放到析构里

        delete td; // 注意释放

        return nullptr;
    }

    const std::string GetFuncName(int sockfd)
    {
        char buffer[1024];
        int n = read(sockfd, buffer, sizeof buffer - 1);
        if (n > 0)
        {
            buffer[n] = 0;
        }
        else if (n == 0) // 读完，对端关闭
        {
            lg.LogMessage(Info, "client quit...\n");
        }
        else
        {
            lg.LogMessage(Error, "read sockfd error, errno: %d, error: %s\n", errno, strerror(errno));
        }
        return buffer;
    }

    void Routine(int sockfd, const InetAddr& addr)
    {
        std::string func_name = GetFuncName(sockfd);
        lg.LogMessage(Debug, "%s selcted %s\n", addr.GetDebug().c_str(), func_name.c_str());
        if(func_name == "ping")
            funcs[func_name](sockfd, addr);
        else if(func_name == "translate")
            funcs[func_name](sockfd, addr);
        else if(func_name == "transform")
            funcs[func_name](sockfd, addr);
        else
        {}

        close(sockfd);  //创建出来发现是4545，所以我猜测accept是在没有连接就已经预定sockfd了
        lg.LogMessage(Debug, "%s colse %d\n", addr.GetDebug().c_str(), sockfd);
    }
    
    void RegisterFunc(const std::string &name, callback_t func)
    {
        funcs[name] = func;
    }

private:
    uint16_t _port;
    int _listensockfd;

    // 构建业务(任务)
    std::unordered_map<std::string, callback_t> funcs; //key不是const
};